package dstree

import (
	"fmt"
	"math"
)

func Calc_Mean(series []Ts_type, start, end int16) Ts_type {
	var mean Ts_type

	if start >= end {
		fmt.Printf("error: start >= end series[%v]\n", series[end-1])
	} else {
		for i := start; i < end; i++ {
			if i >= 128 {
				fmt.Printf("i>128 out %v\n", series)
			}
			mean += series[i]
		}

		mean /= Ts_type(end - start)
	}

	return mean
}
func Calc_mean_per_segment(series []Ts_type, segments []int16) []Ts_type {
	var start int16 = 0
	means := make([]Ts_type, len(segments))
	for i := 0; i < len(segments); i++ {
		end := segments[i]
		means[i] = Calc_Mean(series, start, end)
		start = end
	}
	return means
}

func Calc_Stdev(series []Ts_type, start, end int16) Ts_type {
	var sumXSquares, sumX, stdev Ts_type

	if start >= end {
		fmt.Printf("error: start >= end,start=%d,end=%d", start, end)
	} else {
		countX := end - start // series size

		for i := start; i < end; i++ {
			sumX += series[i]
			sumXSquares += Ts_type(math.Pow(float64(series[i]), 2))
		}

		sumXSquares -= Ts_type(math.Pow(float64(sumX), 2) / float64(countX))

		stdev = Ts_type(math.Sqrt(float64(sumXSquares) / float64(countX)))
	}

	return stdev
}

func Calc_stdev_per_segment(series []Ts_type, segments []int16) []Ts_type {
	var start int16 = 0
	stdevs := make([]Ts_type, len(segments))
	for i := 0; i < len(segments); i++ {
		end := segments[i]
		stdevs[i] = Calc_Stdev(series, start, end)
		start = end
	}
	return stdevs
}

func Calc_split_points(ts_length, segment_size int) (points []int16) {

	points = make([]int16, segment_size)
	avg_length := ts_length / segment_size

	for i := 0; i < segment_size; i++ {
		points[i] = int16(((i + 1) * avg_length))
	}
	//set the last one
	points[segment_size-1] = int16(ts_length)
	return points
}

// 在把每一个segment又平分
func Calc_hs_split_points(split_points []int16, segment_size, min_length int) (hs_split_points []int16) {
	hs_split_points = make([]int16, 0)
	for i := 0; i < segment_size; i++ {
		length := split_points[i] //i==0
		if i > 0 {
			length = int16(split_points[i] - split_points[i-1])
		}
		if length >= int16(min_length*2) {
			start := int16(0)
			if i > 0 {
				start = split_points[i-1]
			}

			hs_split_points = append(hs_split_points, start+(length/2))

		}

		hs_split_points = append(hs_split_points, split_points[i])
	}
	// fmt.Printf("Calc_hs_split_points len(hs_split_points)=%d\n", len(hs_split_points))
	return hs_split_points
}

func Get_Segment_Start(points []int16, idx int) int16 {
	if idx == 0 {
		return 0
	} else {
		return points[idx-1]
	}
}

func Get_Segment_End(points []int16, idx int) int16 {
	return points[idx]
}

func Get_Segment_Length(points []int16, idx int) int16 {
	if idx == 0 {
		return points[idx]
	} else {
		return points[idx] - points[idx-1]
	}
}

func Get_hs_split_point(points []int16, from, to int16) int16 {
	for i := 0; i < len(points); i++ {
		if points[i] == to {
			return from
		}
	}
	return to
}

func Range_calc(sketch Segment_Sketch, len int16) Ts_type {
	mean_width := sketch.Indicators[0] - sketch.Indicators[1]
	stdev_upper := sketch.Indicators[2]
	//stdev_lower := sketch.Indicators[3]
	r := Ts_type(len) * (mean_width*mean_width + stdev_upper*stdev_upper)
	return r
}

func ts_euclidean_distance_reordered(q, t []Ts_type) Ts_type {
	var sum Ts_type
	sum = 0
	len := len(t)
	for i := 0; i < len; i++ {
		//从什么地方开始计算，如果是子序列匹配
		//全匹配 order=0123
		//x := t[order[i]]
		x := t[i]
		sum += (x - q[i]) * (x - q[i])
	}
	return sum
}

func Calculate_node_min_distance(node *Dstree_Node, query []Ts_type) Ts_type {
	var sum Ts_type = 0

	mean_per_segment := Calc_mean_per_segment(query, node.Node_Points)
	stdev_per_segment := Calc_stdev_per_segment(query, node.Node_Points)

	for i, sketch := range node.Segment_Sketches {
		var temp_dist Ts_type = 0
		if (stdev_per_segment[i]-sketch.Indicators[2])*(stdev_per_segment[i]-sketch.Indicators[3]) > 0 {
			temp_dist += Ts_type(math.Pow(float64(min(abs(stdev_per_segment[i]-sketch.Indicators[2]), abs(stdev_per_segment[i]-sketch.Indicators[3]))), 2))
		}

		if (mean_per_segment[i]-sketch.Indicators[0])*(mean_per_segment[i]-sketch.Indicators[1]) > 0 {
			temp_dist += Ts_type(math.Pow(float64(min(abs(mean_per_segment[i]-sketch.Indicators[0]), abs(mean_per_segment[i]-sketch.Indicators[1]))), 2))
		}
		sum += temp_dist * Ts_type(Get_Segment_Length(node.Node_Points, i))
	}
	return sum
}

func Calculate_node_distance(node *Dstree_Node, query_ts []Ts_type,
	bsf Ts_type) (Ts_type, []Ts_type) {
	//offset = 0 是全匹配
	//dist := MaxtsType
	dist, ts := Calculate_ts_in_node_distance(node, query_ts, bsf)

	return dist, ts

}

func Calculate_ts_in_node_distance(
	node *Dstree_Node,
	query_ts []Ts_type,
	bound Ts_type) (Ts_type, []Ts_type) {

	bsf := bound
	bsf_series := make([]Ts_type, len(query_ts))
	var temp_dist Ts_type
	len := len(*node.data)
	for i := 0; i < len; i++ {
		temp_dist = ts_euclidean_distance_reordered(query_ts, (*node.data)[i])
		if temp_dist < bsf {
			bsf = temp_dist
			copy(bsf_series, (*node.data)[i])
		}
	}
	return bsf, bsf_series
}

func Calculate_ts_Set_distance(ts_Set [][]Ts_type, query_ts []Ts_type) Ts_type {
	var bsf Ts_type = MaxtsType
	len := len(ts_Set)
	var temp_dist Ts_type
	for i := 0; i < len; i++ {
		temp_dist = ts_euclidean_distance_reordered(query_ts, ts_Set[i])
		if temp_dist < bsf {
			bsf = temp_dist
		}
	}
	return bsf
}
func Calculate_ts_Set_knn_distance(data [][]Ts_type, query_ts []Ts_type, k int, knn_results PqItemSeries) {
	kth_bsf := knn_results[len(knn_results)-1].Distance //队尾元素距离,最大
	var temp_dist Ts_type
	n := len(data)
	// fmt.Printf("--------Calculate_node_knn_distance in data set---------\n\n")
	for i := 0; i < n; i++ {
		temp_dist = ts_euclidean_distance_reordered(query_ts, data[i])
		if temp_dist < kth_bsf {
			object_result := Query_result{Distance: temp_dist}
			object_result.Series = data[i]
			// fmt.Printf("update results series=%v\n", object_result.Series)
			knn_results.Insert_Sort_results(object_result)
			kth_bsf = knn_results[len(knn_results)-1].Distance
		}

	}
}
func Calculate_node_knn_distance(node *Dstree_Node, query_ts []Ts_type, k int, knn_results PqItemSeries) {
	kth_bsf := knn_results[len(knn_results)-1].Distance //队尾元素距离,最大
	var temp_dist Ts_type
	n := len(*node.data)
	// fmt.Printf("--------Calculate_node_knn_distance in node---------\n\n")
	for i := 0; i < n; i++ {
		temp_dist = ts_euclidean_distance_reordered(query_ts, (*node.data)[i])
		if temp_dist < kth_bsf {
			object_result := Query_result{Node: node, Distance: temp_dist}
			object_result.Series = (*node.data)[i]
			// fmt.Printf("update results series=%v\n", object_result.Series)
			knn_results.Insert_Sort_results(object_result)
			kth_bsf = knn_results[len(knn_results)-1].Distance
		}

	}
}

func Result_is_equal(a []Ts_type, b []Ts_type) bool {
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func Calc_trueN(app_res PqItemSeries, exac_res PqItemSeries, k int) int {
	count := 0
	for i := 0; i < k; i++ {
		for j := 0; j < k; j++ {
			if app_res[i].Distance == exac_res[j].Distance {
				count++
				break
			}
		}
	}
	return count
}

func Calc_recall(app_res PqItemSeries, exac_res PqItemSeries, k int) Ts_type {
	count := Calc_trueN(app_res, exac_res, k)
	return Ts_type(count) / Ts_type(k)
}

func Calc_Avgprec(app_res PqItemSeries, exac_res PqItemSeries, k int) Ts_type {
	var sum Ts_type = 0
	for i := 0; i < k; i++ {
		if app_res[i].Distance == exac_res[i].Distance {
			sum += Calc_recall(app_res, exac_res, i)
		}
	}
	return sum / Ts_type(k)
}

func Calc_relativerr(query []Ts_type, app_res PqItemSeries, exac_res PqItemSeries, k int) Ts_type {
	var sum Ts_type = 0
	for i := 0; i < k; i++ {
		appdis := ts_euclidean_distance_reordered(query, app_res[i].Series)
		exadis := ts_euclidean_distance_reordered(query, exac_res[i].Series)
		sum += (appdis - exadis) / exadis
	}
	return Ts_type(sum / Ts_type(k))
}

func ts_equal(a []Ts_type, b []Ts_type) bool {
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func min(a, b Ts_type) Ts_type {
	if a < b {
		return a
	} else {
		return b
	}
}

func max(a, b Ts_type) Ts_type {
	if a < b {
		return b
	} else {
		return a
	}
}

func abs(a Ts_type) Ts_type {
	if a > 0 {
		return a
	} else {
		return -a
	}
}
